LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;


ENTITY inst_fetch IS
PORT
	(
		clk		           : IN STD_LOGIC;
		reset           : in std_logic;
		-- inst fetch from sram
		sram_read_addr  : in std_logic_vector(17 DOWNTO 0);
		sram_read_data  : in std_logic_vector(15 DOWNTO 0);
		sram_read_valid : IN std_logic;
		
		read_request       : out std_logic;
		read_request_addr  : out std_logic_vector(17 downto 0);
		
		zeroflag        : in std_logic;
		finished_delay  : in std_logic;
		
		inst_ram_output : out unsigned(31 downto 0);
		
		-- fetch data to load unit
		request_addr    : out unsigned(6 downto 0);
		request_enable  : out std_logic
		
	);
END inst_fetch;



ARCHITECTURE bhv OF inst_fetch IS

   signal	rdaddress		:  unsigned (6 DOWNTO 0) := (others => '0');
   signal	old_rdaddress		:  unsigned (6 DOWNTO 0) := (others => '0');
   signal q  : std_logic_vector(35 downto 0);
   signal q_buffer  : std_logic_vector(35 downto 0) := (others => '0');
   signal	rdaddress_wire		:  unsigned (6 DOWNTO 0);
   
   signal inst_buffer : std_logic_vector(35 downto 0) := (others => '1');
   signal inst_request_addr_buffer : std_logic_vector(17 DOWNTO 0) := (others => '0');
   signal addr_buffer_1_clock : std_logic_vector(17 DOWNTO 0) := (others => '0');
   signal h_l : std_logic := '0';
   
   signal request_addr_buffer : unsigned(6 downto 0) := (others => '0');
   signal request_enable_buffer : std_logic := '0';
   
   signal inst_output_enable : std_logic := '0';
   signal stop_internal_fetch : std_logic := '0';
   signal start : std_logic := '0';
    
   signal next_valid : std_logic := '0'; 
   signal fetch_done : std_logic := '1';
   signal jumped : std_logic := '0';
   
   signal delay_pending : std_logic := '0';
   
   
    
   component instruction_ram IS
	 PORT
	 (
		clock		: IN STD_LOGIC;
		data		: IN STD_LOGIC_VECTOR (35 DOWNTO 0);
		rdaddress		: IN STD_LOGIC_VECTOR (6 DOWNTO 0);
		wraddress		: IN STD_LOGIC_VECTOR (6 DOWNTO 0);
		wren		: IN STD_LOGIC  := '1';
		q		: OUT STD_LOGIC_VECTOR (35 DOWNTO 0)
	 );
   END component;
  

BEGIN
  
  
  instruction_ram1 : instruction_ram
	PORT map
	(
		clock		=> clk,
		data		=> inst_buffer,
		rdaddress		=> std_logic_vector(rdaddress_wire),
		wraddress		=> std_logic_vector(inst_request_addr_buffer(7 downto 1)),
		wren		=> inst_output_enable,
		q		=> q
	);
	
	
	
	-- fetch if not available
	process (clk)
	begin
	  if rising_edge(clk) then
	    
	    inst_output_enable <= '0';
	    
	    if reset='1' then
	    
	     inst_buffer <= (others => '0');
	     --inst_request_addr_buffer <= (others => '0');
	     start<='1';
	    
	    else
	      
	      if q_buffer(35 downto 32)="0000" then
	         if stop_internal_fetch='0' and fetch_done='1' then
	            addr_buffer_1_clock <= (17 downto 8 => '0') & std_logic_vector(old_rdaddress) & '0';
	            inst_buffer(35 downto 32)<="0000";
	            h_l<='0';
	            fetch_done<='0';
	         end if; 
	      end if; 
	      
	      if h_l='0' then 
	        inst_request_addr_buffer <= addr_buffer_1_clock; 
	      end if;
	      
	      if inst_buffer(35 downto 32)="0000" and start='1'  then
		     
	       if inst_request_addr_buffer=sram_read_addr and sram_read_valid='1' then
	          if h_l='0' then
	            inst_buffer(31 downto 16)<= sram_read_data;
	            h_l<='1';
	            inst_request_addr_buffer <= std_logic_vector(unsigned(inst_request_addr_buffer) +1);
	          else
	            inst_buffer(15 downto 0)<= sram_read_data;
	            inst_buffer(35 downto 32)<="0001";
	            inst_output_enable <= '1';
	            fetch_done<='1';
	          end if;
	       end if;
	     end if;
	    
	    end if;
	    
	  end if;
	end process;
	
	
	
	process (clk)
	begin
	 if rising_edge(clk) then
	   
	   q_buffer <= q;
	   rdaddress <= rdaddress_wire;
	   
	   jumped<='0';
	   stop_internal_fetch<='0';
	   next_valid<='0';
	   
	   if finished_delay='1' then
	     delay_pending <= '0';
	   end if;
	   
	   if q_buffer(35 downto 32)="0000" then
	     
	     stop_internal_fetch<='1';
	     
	     if jumped='0' then 
	       --rdaddress<=old_rdaddress;
	     end if;
	     
	   else
	      
	     
	     old_rdaddress<=rdaddress;
	     
	     if jumped='0' and delay_pending='0' then
	   
	       case q_buffer(31 downto 30) is
	   
	         when "01" =>
	           --rdaddress <= rdaddress +1;
	       
	         when "10" =>
	          if zeroflag='0' then
	            --rdaddress <= unsigned(q(6 downto 0));
	            jumped<='1'; 
	          else
	           --rdaddress <= rdaddress +1;
	         end if;
	   
	         when "11" =>
	           if stop_internal_fetch='0' then
	             delay_pending <= '1';
	             --rdaddress <= rdaddress + 1;
	           end if;
	   
	         when others =>
	     
	       end case;
	    
	       --if q(31 downto 29)="001" then  
	       --  request_addr_buffer <= unsigned(q(6 downto 0));
	       --  request_enable_buffer <= '1';
	       --else
	       --  request_enable_buffer <= '0';
	       --end if;

	     else
	       
	       if jumped='1' then
	         --rdaddress <= rdaddress +1;
	       end if;
	       
	     end if;
	   
	   end if;
	     
	 end if;
	end process;
	
	
	rdaddress_wire <= old_rdaddress when jumped='0' and q_buffer(35 downto 32)="0000" else
	            rdaddress+1 when (not (q_buffer(35 downto 32)="0000")) and q_buffer(31 downto 30)="01" and jumped='0' and delay_pending='0' else
	            unsigned(q_buffer(6 downto 0)) when (not (q_buffer(35 downto 32)="0000")) and q_buffer(31 downto 30)="10" and zeroflag='0' and jumped='0' and delay_pending='0' else
	            rdaddress+1 when (not (q_buffer(35 downto 32)="0000")) and q_buffer(31 downto 30)="11" and stop_internal_fetch='0' and jumped='0' and delay_pending='0' else
	            rdaddress +1 when (not (q_buffer(35 downto 32)="0000")) and jumped='1' else
	            rdaddress;
	
	
	
	inst_ram_output <= --unsigned(inst_buffer(31 downto 16) & sram_read_data) when inst_output_enable='1' else
	                   unsigned(q_buffer(31 downto 0)) when unsigned(q_buffer(35 downto 32))>0 and stop_internal_fetch='0' and jumped='0' and delay_pending='0' else
	                   (31 downto 0 => '0');
	 
	read_request       <= '1' when inst_buffer(35 downto 32)="0000" and start='1' else '0';
  read_request_addr  <= inst_request_addr_buffer;             
	               
	                   
	-- to load unit
	request_addr <= request_addr_buffer;
	request_enable <= '1' when q(31 downto 29)="001" and request_enable_buffer='0' else '0';

END bhv;








